﻿using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using SharedTypes;
using System;
using AdaptationProject.Forms;

namespace AdaptGenericAlgorithm
{
    // Обработка дерева параметров и запуск исполнительного файла 
    [AlgInfo("AdaptGeneric algorithm")]
    public class AdaptGenericAlgorithm : Algorithm
    {
        private string compressionTargetDirectory;
        private string compressionTargetTarName;
        private string compressionTargetArchName;

        public AdaptGenericAlgorithm(string exeName, string name, string path, string fullPathXml, 
                                     IDictionary<int, LinkedParameter> parameters)
            : base(exeName, name, path, fullPathXml, parameters)
        {
            
        }

        string GetNormalSeparator(KeyValuePair<int, LinkedParameter> param)
        {
            return param.Value.parameter.Separator == "\"\"" ? " " : param.Value.parameter.Separator;
        }
        // Tar file
        bool IsContainHelpFullFile(IDictionary<int, LinkedParameter> parameters)
        {
            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parameter.Type == ParamType.HELPFILE)
                {
                    return true;
                }
            }
            return false;
        }

        // Tar file
        string GetHelpFullArg(IDictionary<int, LinkedParameter> parameters)
        {
            string arguments = "";

            int number = 0;
            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parentKey == -1) // isn't linked
                {
                    number++;
                    continue;
                }

                if (number == 1) // Crutch
                    // если первый параметр (т.е. "а")
                {
                    arguments += param.Value.parameter.CurrentValue.ToString();
                    if (param.Value.parameter.ValueEnd.Length != 0)
                    {
                        arguments += param.Value.parameter.ValueEnd;
                    }
                    arguments += GetNormalSeparator(param);
                }
                else if (param.Value.parameter.Type == ParamType.HELPFILE)
                {
                    // Установка файла исследования
                    compressionTargetTarName = param.Value.parameter.CurrentValue.ToString();
                    arguments += "\"" + param.Value.parameter.CurrentValue.ToString() + "\"";
                    arguments += GetNormalSeparator(param);
                }
                else if (param.Value.parameter.Type == ParamType.PATH)
                {
                    string path = param.Value.parameter.CurrentValue.ToString();

                    // Установка папки исследования
                    compressionTargetDirectory = path;

                    int index = path.LastIndexOf("\\");
                    if (index == (path.Length - 1))
                    {
                        arguments += "\"" + path.Substring(0, index) + "\"";
                    }
                    else
                    {
                        arguments += "\"" + path + "\"";
                    }
                    arguments += GetNormalSeparator(param);
                }
                ++number;
            }

            return arguments.Substring(0, arguments.LastIndexOf(" "));
        }

        // Tar file
        string GetHelpFullArgRec(IDictionary<int, LinkedParameter> parameters)
        {
            string arguments = "";

            int number = 0;
            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parentKey == -1) // isn't linked
                {
                    ++number;
                    continue;
                }

                if (number == 1) // Crutch
                // если первый параметр (т.е. "а")
                {
                    arguments += param.Value.parameter.DefaultValue.ToString();
                    if (param.Value.parameter.ValueEnd.Length != 0)
                    {
                        arguments += param.Value.parameter.ValueEnd;
                    }
                    arguments += GetNormalSeparator(param);
                }
                else if (param.Value.parameter.Type == ParamType.HELPFILE)
                {
                    // Установка файла исследования
                    compressionTargetTarName = param.Value.parameter.AcceptableValues[0].TextValues.ToString();
                    arguments += "\"" + param.Value.parameter.AcceptableValues[0].TextValues.ToString() + "\"";
                    arguments += GetNormalSeparator(param);
                }
                else if (param.Value.parameter.Type == ParamType.PATH)
                {
                    string path = param.Value.parameter.AcceptableValues[0].TextValues.ToString();

                    // Установка папки исследования
                    compressionTargetDirectory = path;

                    int index = path.LastIndexOf("\\");
                    if (index == (path.Length - 1))
                    {
                        arguments += "\"" + path.Substring(0, index) + "\"";
                    }
                    else
                    {
                        arguments += "\"" + path + "\"";
                    }
                    arguments += GetNormalSeparator(param);
                }
                ++number;
            }

            return arguments.Substring(0, arguments.LastIndexOf(" "));
        }

        /// <summary>
        /// Получение строки с параметров взависимости от типа параметра.
        /// </summary>
        /// <param name="parameters">Параметр.</param>
        /// <returns>Строка с параметром.</returns>
        /// 
        string GetStringParamFromTypeFull(KeyValuePair<int, LinkedParameter> param,
                                          IDictionary<int, LinkedParameter> parameters)
        {
            bool isContainHelpFullFile = IsContainHelpFullFile(parameters);
            string arguments = "";
            if (param.Value.parameter.IsParamKey)
            {
                arguments += param.Value.parameter.Name + param.Value.parameter.CurrentValue.ToString();
                // Получение подпараметров
                arguments += GetNormalSeparator(param);
                arguments += GetLinkedParam(param, parameters);
                if (param.Value.parameter.ValueEnd.Length != 0)
                {
                    arguments += param.Value.parameter.ValueEnd;
                }
                
            }
            else if (param.Value.parameter.Type == ParamType.FILE)
            {
                // Установка файла исследования
                compressionTargetArchName = param.Value.parameter.CurrentValue.ToString();
                arguments += "\"" + param.Value.parameter.CurrentValue.ToString() + "\"";
                arguments += GetNormalSeparator(param);
            }
            else if (param.Value.parameter.Type == ParamType.HELPFILE && isContainHelpFullFile)
            {
                // Установка файла исследования
                compressionTargetTarName = param.Value.parameter.CurrentValue.ToString();
                arguments += "\"" + param.Value.parameter.CurrentValue.ToString() + "\"";
                arguments += GetNormalSeparator(param);
            }
            else if (param.Value.parameter.Type == ParamType.PATH && !isContainHelpFullFile)
            {
                string path = param.Value.parameter.CurrentValue.ToString();

                // Установка папки исследования
                compressionTargetDirectory = path;

                int index = path.LastIndexOf("\\");
                if (index == (path.Length - 1))
                {
                    arguments += "\"" + path.Substring(0, index) + "\"";
                }
                else
                {
                    arguments += "\"" + path + "\"";
                }
                arguments += GetNormalSeparator(param);
            }
            else if (param.Value.parameter.Type != ParamType.PATH)
            {
                arguments += param.Value.parameter.CurrentValue.ToString();
                // Получение подпараметров
                arguments += GetNormalSeparator(param);
                arguments += GetLinkedParam(param, parameters);
                if (param.Value.parameter.ValueEnd.Length != 0)
                {
                    arguments += param.Value.parameter.ValueEnd;
                }
                
            }
            return arguments;
        }

        /// <summary>
        /// Получение связанных параметров.
        /// </summary>
        /// <param name="parameters">Параметр, список параметров.</param>
        /// <returns>Строка параметров.</returns>
        string GetLinkedParamDepth(KeyValuePair<int, LinkedParameter> linkedParam,
                                   IDictionary<int, LinkedParameter> parameters)
        {
            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parameter.ConditionLink.Length == 0)
                {
                    // если связанные параметры
                    if (param.Value.parentKey == linkedParam.Key)
                    {
                        // получение значение и подуровня
                        return GetLinkedParamDepth(param, parameters);
                    }
                }
                else
                {
                    // если связанные параметры
                    if (param.Value.parentKey == linkedParam.Key &&
                        linkedParam.Value.parameter.CurrentValue.ToString().Length != 0 &&
                        linkedParam.Value.parameter.CurrentValue.ToString() == param.Value.parameter.ConditionLink)
                    {
                        // получение значение и подуровня
                        return  GetLinkedParamDepth(param, parameters);
                    }
                }
            }
            return "";
        }

        /// <summary>
        /// Получение связанных параметров.
        /// </summary>
        /// <param name="parameters">Параметр, список параметров.</param>
        /// <returns>Строка параметров.</returns>
        string GetLinkedParam(KeyValuePair<int, LinkedParameter> linkedParam,
                              IDictionary<int, LinkedParameter> parameters)
        {
            string result = "";
            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parameter.ConditionLink.Length == 0)
                {
                    // если связанные параметры
                    if (param.Value.parentKey == linkedParam.Key)
                    {
                        // получаем значение и связанные с ним параметры самого нижнего уровня
                        result += GetStringParamFromTypeFull(param, parameters) + GetLinkedParamDepth(param, parameters);
                    }
                }
                else
                {
                    // если связанные параметры
                    if (param.Value.parentKey == linkedParam.Key &&
                        linkedParam.Value.parameter.CurrentValue.ToString() == param.Value.parameter.ConditionLink)
                    {
                        // получаем значение и связанные с ним параметры самого нижнего уровня
                        result += GetStringParamFromTypeFull(param, parameters) + GetLinkedParamDepth(param, parameters);
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Получение связанных параметров.
        /// </summary>
        /// <param name="parameters">Параметр, список параметров.</param>
        /// <returns>Строка параметров.</returns>
        string GetLinkedParamReccomend(KeyValuePair<int, LinkedParameter> linkedParam,
                              IDictionary<int, LinkedParameter> parameters)
        {
            string result = "";
            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parameter.ConditionLink.Length == 0)
                {
                    // если связанные параметры
                    if (param.Value.parentKey == linkedParam.Key)
                    {
                        // получаем значение и связанные с ним параметры самого нижнего уровня
                        result += GetStringParamFromTypeRecommend(param, parameters) + GetLinkedParamDepth(param, parameters);
                    }
                }
                else
                {
                    // если связанные параметры
                    if (param.Value.parentKey == linkedParam.Key &&
                        linkedParam.Value.parameter.CurrentValue.ToString() == param.Value.parameter.ConditionLink)
                    {
                        // получаем значение и связанные с ним параметры самого нижнего уровня
                        result += GetStringParamFromTypeRecommend(param, parameters) + GetLinkedParamDepth(param, parameters);
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Запускает процесс исполнительного файла.
        /// </summary>
        /// <param name="parameters">Список применяемых параметров.</param>
        /// <returns>Размер полученого архива.</returns>
        public override long DoCompression(IDictionary<int, LinkedParameter> parameters)
        {
            string arguments = "";
            bool isContainHelpFullFile = IsContainHelpFullFile(parameters);

            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parentKey == -1 || // если нет связи
                    param.Value.parentKey != 0) // пропуск нижнего уровня так как он уже обработан
                {
                    continue;
                }

                // Обработка только верхнего уровня
                arguments += GetStringParamFromTypeFull(param, parameters);
            }
            arguments = arguments.Substring(0, arguments.LastIndexOf(" "));

            if (isContainHelpFullFile)
            {
                string helpFuleArg = GetHelpFullArg(parameters);
                RunExe(helpFuleArg);
            }

            return RunExe(arguments);
        }


        /// <summary>
        /// Устанавливает директорию, исполнительного файла которой будет выполняться.
        /// </summary>
        /// <param name="path">Абсолютный путь к директории.</param>
        public override void SetCompressionTarget(string path)
        {
            compressionTargetDirectory = path;
            compressionTargetArchName = path + @"\test.rar";
        }

        /// <summary>
        /// Получение строки с параметров взависимости от типа параметра.
        /// </summary>
        /// <param name="parameters">Параметр.</param>
        /// <returns>Строка с параметром.</returns>
        /// 
        string GetStringParamFromTypeRecommend(KeyValuePair<int, LinkedParameter> param,
                                               IDictionary<int, LinkedParameter> parameters)
        {
            bool isContainHelpFullFile = IsContainHelpFullFile(parameters);
            string arguments = "";

            if (param.Value.parameter.IsParamKey)
            {
                arguments += param.Value.parameter.Name + param.Value.parameter.DefaultValue.ToString();
                arguments += GetNormalSeparator(param);
                // Берем только default value
                /*arguments += GetLinkedParam(param, parameters);*/
                if (param.Value.parameter.ValueEnd.Length != 0)
                {
                    arguments += param.Value.parameter.ValueEnd;
                }
            }
            else if (param.Value.parameter.Type == ParamType.FILE)
            {
                // Установка файла исследования
                compressionTargetArchName = param.Value.parameter.AcceptableValues[0].TextValues.ToString();
                arguments += "\"" + param.Value.parameter.AcceptableValues[0].TextValues.ToString() + "\"";
                arguments += GetNormalSeparator(param);
            }
            else if (param.Value.parameter.Type == ParamType.HELPFILE && isContainHelpFullFile)
            {
                // Установка файла исследования
                compressionTargetTarName = param.Value.parameter.AcceptableValues[0].TextValues.ToString();
                arguments += "\"" + param.Value.parameter.AcceptableValues[0].TextValues.ToString() + "\"";
                arguments += GetNormalSeparator(param);
            }
            else if (param.Value.parameter.Type == ParamType.PATH && !isContainHelpFullFile)
            {
                string path = param.Value.parameter.AcceptableValues[0].TextValues.ToString();

                // Установка папки исследования
                compressionTargetDirectory = path;

                int index = path.LastIndexOf("\\");
                if (index == (path.Length - 1))
                {
                    arguments += "\"" + path.Substring(0, index) + "\"";
                }
                else
                {
                    arguments += "\"" + path + "\"";
                }
                arguments += GetNormalSeparator(param);
            }
            else if (param.Value.parameter.Type != ParamType.PATH)
            {
                IList<object> tmp = (IList<object>)param.Value.parameter.AcceptableValues[0].values;
                arguments += tmp[0].ToString();
                arguments += GetNormalSeparator(param);
                arguments += GetLinkedParamReccomend(param, parameters);
                if (param.Value.parameter.ValueEnd.Length != 0)
                {
                    arguments += param.Value.parameter.ValueEnd;
                }
            }

            return arguments;
        }

        /// <summary>
        /// Запускает процесс исполнительного файла с параметрами, рекомендуемыми поставщиком
        /// алгоритма исполнительного файла.
        /// </summary>
        /// <param name="parameters">Список применяемых параметров.</param>
        /// <returns>Размер полученного архива.</returns>
        public override long GetResultWithRecomendedParameters(IDictionary<int, LinkedParameter> parameters)
        {
            string arguments = "";
            object emptyObject = (object)"";
            
            foreach (KeyValuePair<int, LinkedParameter> param in parameters)
            {
                if (param.Value.parameter.DefaultValue.Equals(emptyObject))
                {
                    continue;
                }

                if (param.Value.parentKey == -1 || // если нет связи
                    param.Value.parentKey != 0) // пропуск нижнего уровня так как он уже обработан
                {
                    continue;
                }

                // Обработка только верхнего уровня
                arguments += GetStringParamFromTypeRecommend(param, parameters);
            }
            arguments = arguments.Substring(0, arguments.LastIndexOf(" "));
            
            if (IsContainHelpFullFile(parameters))
            {
                string helpFuleArg = GetHelpFullArgRec(parameters);
                RunExe(helpFuleArg);
            }

            return RunExe(arguments);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="arguments"></param>
        /// <returns></returns>
        private long RunExe(string arguments)
        {
            long result = 0;
            DirectoryInfo dir = new DirectoryInfo(compressionTargetDirectory);

//             if (dir.Exists)
//             {
                ProcessStartInfo info = new ProcessStartInfo(ExeName, arguments);
                info.UseShellExecute = false;
                info.CreateNoWindow = true;
                Process proc = new Process();
                proc.StartInfo = info;
                DateTime startTime = DateTime.Now;
                proc.Start();
                proc.WaitForExit();
                //proc.ExitCode;
                DateTime endTime = DateTime.Now;
                TimeElapsed = endTime.TimeOfDay.Subtract(startTime.TimeOfDay);

                if (File.Exists(compressionTargetArchName))
                {
                    result = File.ReadAllBytes(compressionTargetArchName).Length;
                    File.Delete(compressionTargetArchName);
                    if (compressionTargetTarName != null && compressionTargetTarName.Length != 0)
                    {
                        File.Delete(compressionTargetTarName);
                    }
                }
         //   }

            return result;
        }
    }
}
